/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */

package l1j.server.server.datatables;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.server.DatabaseFactory;
import l1j.server.server.templates.L1Exp;
import l1j.server.server.utils.PerformanceTimer;
import l1j.server.server.utils.SQLUtil;

/**
 * 経験値テーブルを提供するクラス
 */
public final class ExpTable {

	private static ExpTable _instance;

	private static final Logger _log = Logger.getLogger(ExpTable.class.getName());

//	public static final int MAX_LEVEL = 99;
//	public static final int MAX_EXP = 0x6ca8c796;
	public static int MAX_LEVEL = 0;

	public static long MAX_EXP = 0;

	private static final HashMap<Integer, L1Exp> _expList = new HashMap<Integer, L1Exp>();


	private static HashMap<Integer , Double> _levelMet =new HashMap<Integer , Double>();

	private static HashMap<Integer , Integer> _levelMet2 =new HashMap<Integer , Integer>();


	private static HashMap<Integer , Double> _levelBonus = new HashMap<Integer , Double>();

	/**取回死亡時扣除經驗值*/
	public static long getdeathPenalty(final int level){
		final L1Exp l1exp = _expList.get(new Integer(level));
		long exp = getNeedExpNextLevel(level);
		exp *= l1exp.get_deathPenalty();

		if(exp >=1){
			return exp;
		}
		return 0;

	}

	/**
	 * 指定等級所需要的經驗直
	 *
	 * @param level
	 * @return 所需要的經驗直
	 */
	public static long getExpByLevel(final int level) {
		final L1Exp l1exp = _expList.get(new Integer(level - 1));
		long exp = 0;
		if (l1exp != null) {
			exp = l1exp.get_exp();
		}
		return exp;
	}

	/**
	 * 經驗直百分比(寵物等級顯示使用)
	 * @param level
	 * @param exp
	 * @return
	 */
	public static int getExpPercentage(final int level, final long l) {
		return (int) (100.0 * ((double) (l - getExpByLevel(level)) / (double) getNeedExpNextLevel(level)));
	}

	public static ExpTable getInstance() {
		if(_instance ==null){
			_instance = new ExpTable();
			_instance.ExpLoad();
		}
		return _instance;
	}

	public static double getLevelBouns(final int level){

		if(_levelBonus.get(level) ==null){
			return 1.0;
		}

		return _levelBonus.get(level);
	}


	/**
	 * 累積經驗直對應的等級
	 *
	 * @param exp 累積経験値
	 * @return
	 */
	public static int getLevelByExp(final long exp) {
		int level = 1;
		for (final Integer integer: _expList.keySet()) {
			if (integer == 0) {
				continue;
			}
			final L1Exp up = _expList.get(integer);
			final L1Exp dn = _expList.get(integer - 1);
			final long upe = up.get_exp();
			final long dne = dn.get_exp();
			if (exp >= dne && exp < upe) {
				level = integer;
			}
		}
		return Math.min(level, MAX_LEVEL);
	}


	public static double getLevelMetRate(final int levelmet){

		if(_levelMet.get(levelmet) ==null){
			return 0.00123794;
		}
		return _levelMet.get(levelmet);
	}


	public static int getLevelMetRate2(final int levelmet){
		if(_levelMet2.get(levelmet)==null){
			return 0;
		}

		return _levelMet2.get(levelmet);
	}

	/**取得升級還需要多少經驗值*/
	public static long getLevelUpNeedExp(final long l, final int level){//10000
		final long ExpByLevel = getExpByLevel(level);//5000
		return getNeedExpNextLevel(level)-(l-ExpByLevel);//45000 - 5000
	}
	/**
	 * 下一個等級需要的經驗直
	 *
	 * @param level
	 * @return 所需要的經驗直
	 */
	public static long getNeedExpNextLevel(final int level) {
		return getExpByLevel(level + 1) - getExpByLevel(level);
	}

	/**
	 * 目前等即可取回的經驗直
	 *
	 * @param level
	 * @return
	 */
	public static double getPenaltyRate(final int level) {
		final L1Exp l1exp = _expList.get(new Integer(level));
		double expPenalty = 1.0;
		if (l1exp != null) {
			expPenalty = 1.0 / l1exp.get_expPenalty();
		}
		return expPenalty;
	}
	/**取回可購買的經驗值*/
	public static long getResExpTable(final int level){
	final L1Exp l1exp = _expList.get(new Integer(level));
	long exp = getExpByLevel(level + 1) - getExpByLevel(level);
	 exp *= l1exp.get_resexp();
	 if(exp >=1){
		 return exp;
	 }
		return 0;
	}
	private void ExpLoad(){
		_levelMet.put( new Integer( 0 ), new Double( 1.0) );
		_levelMet.put( new Integer( 1 ) , new Double( 0.8 ) );
		_levelMet.put( new Integer( 2 ) , new Double( 0.8 ) );
		_levelMet.put( new Integer( 3 ) , new Double( 0.8 ) );
		_levelMet.put( new Integer( 4 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 5 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 6 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 7 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 8 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 9 ) , new Double( 0.7 ) );
		_levelMet.put( new Integer( 10 ) , new Double( 0.65 ) );
		_levelMet.put( new Integer( 11 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 12 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 13 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 14 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 15 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 16 ) , new Double( 0.64 ) );
		_levelMet.put( new Integer( 17 ) , new Double( 0.512 ) );
		_levelMet.put( new Integer( 18 ) , new Double( 0.512 ) );
		_levelMet.put( new Integer( 19 ) , new Double( 0.512 ) );
		_levelMet.put( new Integer( 20 ) , new Double( 0.512 ) );
		_levelMet.put( new Integer( 21 ) , new Double( 0.512 ) );
		_levelMet.put( new Integer( 22 ) , new Double( 0.4096 ) );
		_levelMet.put( new Integer( 23 ) , new Double( 0.32768 ) );
		_levelMet.put( new Integer( 24 ) , new Double( 0.262144 ) );
		_levelMet.put( new Integer( 25 ) , new Double( 0.2097152 ) );
		_levelMet.put( new Integer( 26 ) , new Double( 0.16777216 ) );
		_levelMet.put( new Integer( 27 ) , new Double( 0.134217728 ) );
		_levelMet.put( new Integer( 28 ) , new Double( 0.107374182 ) );
		_levelMet.put( new Integer( 29 ) , new Double( 0.085899346 ) );
		_levelMet.put( new Integer( 30 ) , new Double( 0.068719477 ) );


		_levelBonus.put( new Integer( 49 ) , new Double( 1.15 ) );
		_levelBonus.put( new Integer( 50 ) , new Double( 1.14 ) );
		_levelBonus.put( new Integer( 51 ) , new Double( 1.13 ) );
		_levelBonus.put( new Integer( 52 ) , new Double( 1.12 ) );
		_levelBonus.put( new Integer( 53 ) , new Double( 1.11 ) );
		_levelBonus.put( new Integer( 54 ) , new Double( 1.1 ) );
		_levelBonus.put( new Integer( 55 ) , new Double( 1.09 ) );
		_levelBonus.put( new Integer( 56 ) , new Double( 1.08 ) );
		_levelBonus.put( new Integer( 57 ) , new Double( 1.07 ) );
		_levelBonus.put( new Integer( 58 ) , new Double( 1.06 ) );
		_levelBonus.put( new Integer( 59 ) , new Double( 1.05 ) );
		_levelBonus.put( new Integer( 60 ) , new Double( 1.04 ) );
		_levelBonus.put( new Integer( 61 ) , new Double( 1.03 ) );
		_levelBonus.put( new Integer( 62 ) , new Double( 1.02 ) );
		_levelBonus.put( new Integer( 63 ) , new Double( 1.01 ) );
		_levelBonus.put( new Integer( 64 ) , new Double( 1.01 ) );


		_levelMet2.put( new Integer( 60 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 61 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 62 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 63 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 64 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 65 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 66 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 67 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 68 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 69 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 70 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 71 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 72 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 73 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 74 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 75 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 76 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 77 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 78 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 79 ) , new Integer( 1 ) );
		_levelMet2.put( new Integer( 80 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 81 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 82 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 83 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 84 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 85 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 86 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 87 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 88 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 89 ) , new Integer( 2 ) );
		_levelMet2.put( new Integer( 90 ) , new Integer( 4 ) );
		_levelMet2.put( new Integer( 91 ) , new Integer( 4 ) );
		_levelMet2.put( new Integer( 92 ) , new Integer( 4 ) );
		_levelMet2.put( new Integer( 93 ) , new Integer( 4 ) );
		_levelMet2.put( new Integer( 94 ) , new Integer( 4 ) );
		_levelMet2.put( new Integer( 95 ) , new Integer( 5 ) );
		_levelMet2.put( new Integer( 96 ) , new Integer( 5 ) );
		_levelMet2.put( new Integer( 97 ) , new Integer( 5 ) );
		_levelMet2.put( new Integer( 98 ) , new Integer( 5 ) );
		_levelMet2.put( new Integer( 99 ) , new Integer( 5 ) );
		_levelMet2.put( new Integer( 100 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 101 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 102 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 103 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 104 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 105 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 106 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 107 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 108 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 109 ) , new Integer( 6 ) );
		_levelMet2.put( new Integer( 110 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 111 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 112 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 113 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 114 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 115 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 116 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 117 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 118 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 119 ) , new Integer( 7 ) );
		_levelMet2.put( new Integer( 120 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 121 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 122 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 123 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 124 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 125 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 126 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 127 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 128 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 129 ) , new Integer( 8 ) );
		_levelMet2.put( new Integer( 130 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 131 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 132 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 133 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 134 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 135 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 136 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 137 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 138 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 139 ) , new Integer( 9 ) );
		_levelMet2.put( new Integer( 140 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 141 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 142 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 143 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 144 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 145 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 146 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 147 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 148 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 149 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 150 ) , new Integer( 15 ) );
		_levelMet2.put( new Integer( 151 ) , new Integer( 16 ) );
		_levelMet2.put( new Integer( 152 ) , new Integer( 16 ) );
		_levelMet2.put( new Integer( 153 ) , new Integer( 16 ) );
		_levelMet2.put( new Integer( 154 ) , new Integer( 16 ) );
		_levelMet2.put( new Integer( 155 ) , new Integer( 16 ) );
		_levelMet2.put( new Integer( 156 ) , new Integer( 17 ) );
		_levelMet2.put( new Integer( 157 ) , new Integer( 17 ) );
		_levelMet2.put( new Integer( 158 ) , new Integer( 17 ) );
		_levelMet2.put( new Integer( 159 ) , new Integer( 17 ) );
		_levelMet2.put( new Integer( 160 ) , new Integer( 17 ) );
		_levelMet2.put( new Integer( 161 ) , new Integer( 18 ) );
		_levelMet2.put( new Integer( 162 ) , new Integer( 18 ) );
		_levelMet2.put( new Integer( 163 ) , new Integer( 18 ) );
		_levelMet2.put( new Integer( 164 ) , new Integer( 18 ) );
		_levelMet2.put( new Integer( 165 ) , new Integer( 18 ) );
		_levelMet2.put( new Integer( 166 ) , new Integer( 19 ) );
		_levelMet2.put( new Integer( 167 ) , new Integer( 19 ) );
		_levelMet2.put( new Integer( 168 ) , new Integer( 19 ) );
		_levelMet2.put( new Integer( 169 ) , new Integer( 19 ) );
		_levelMet2.put( new Integer( 170 ) , new Integer( 19 ) );
		_levelMet2.put( new Integer( 171 ) , new Integer( 20 ) );
		_levelMet2.put( new Integer( 172 ) , new Integer( 20 ) );
		_levelMet2.put( new Integer( 173 ) , new Integer( 20 ) );
		_levelMet2.put( new Integer( 174 ) , new Integer( 20 ) );
		_levelMet2.put( new Integer( 175 ) , new Integer( 20 ) );
		_levelMet2.put( new Integer( 176 ) , new Integer( 21 ) );
		_levelMet2.put( new Integer( 177 ) , new Integer( 21 ) );
		_levelMet2.put( new Integer( 178 ) , new Integer( 21 ) );
		_levelMet2.put( new Integer( 179 ) , new Integer( 21 ) );
		_levelMet2.put( new Integer( 180 ) , new Integer( 21 ) );
		_levelMet2.put( new Integer( 181 ) , new Integer( 22 ) );
		_levelMet2.put( new Integer( 182 ) , new Integer( 22 ) );
		_levelMet2.put( new Integer( 183 ) , new Integer( 22 ) );
		_levelMet2.put( new Integer( 184 ) , new Integer( 22 ) );
		_levelMet2.put( new Integer( 185 ) , new Integer( 22 ) );
		_levelMet2.put( new Integer( 186 ) , new Integer( 23 ) );
		_levelMet2.put( new Integer( 187 ) , new Integer( 23 ) );
		_levelMet2.put( new Integer( 188 ) , new Integer( 23 ) );
		_levelMet2.put( new Integer( 189 ) , new Integer( 23 ) );
		_levelMet2.put( new Integer( 190 ) , new Integer( 23 ) );
		_levelMet2.put( new Integer( 191 ) , new Integer( 24 ) );
		_levelMet2.put( new Integer( 192 ) , new Integer( 24 ) );
		_levelMet2.put( new Integer( 193 ) , new Integer( 24 ) );
		_levelMet2.put( new Integer( 194 ) , new Integer( 24 ) );
		_levelMet2.put( new Integer( 195 ) , new Integer( 24 ) );
		_levelMet2.put( new Integer( 196 ) , new Integer( 25 ) );
		_levelMet2.put( new Integer( 197 ) , new Integer( 25 ) );
		_levelMet2.put( new Integer( 198 ) , new Integer( 25 ) );
		_levelMet2.put( new Integer( 199 ) , new Integer( 25 ) );
		_levelMet2.put( new Integer( 200 ) , new Integer( 25 ) );


	}
	/**
	 * 死亡時経験値ペナルティテーブル
	 */

	public void load() {
		final PerformanceTimer timer = new PerformanceTimer();
		Connection con = null;
		PreparedStatement pstm = null;
		ResultSet rs = null;
		try {
			con = DatabaseFactory.get().getConnection();
			pstm = con.prepareStatement("SELECT * FROM `exp`");
			rs = pstm.executeQuery();

			L1Exp l1exp;
			while (rs.next()) {
				final int level = rs.getInt("level");
				final long exp = rs.getLong("exp");
				final double expPenalty = rs.getDouble("expPenalty");
				final double resexp = rs.getDouble("resexp");
				final double deathPenalty = rs.getDouble("deathPenalty");
				if (level > MAX_LEVEL) {
					MAX_LEVEL = level;
				}
				if (exp > MAX_EXP) {
					MAX_EXP = exp;
				}
				l1exp = new L1Exp();
				l1exp.set_level(level);
				l1exp.set_exp(exp);
				l1exp.set_expPenalty(expPenalty);
				l1exp.set_resexp(resexp);
				l1exp.set_deathPenalty(deathPenalty);

				_expList.put(new Integer(level), l1exp);
			}

			_log.info("載入經驗質設置資料數量: " + _expList.size() + "(" + timer.get() + "ms)");

		} catch (final SQLException e) {
			_log.log(Level.WARNING,e.getLocalizedMessage(), e);

		} finally {
			SQLUtil.close(rs);
			SQLUtil.close(pstm);
			SQLUtil.close(con);
		}
		MAX_LEVEL -= 1;
	}

}
